package com.lauor.smpedr.spring.utils;


import com.lauor.smpedr.session.SqlSession;
import com.lauor.smpedr.session.SqlSessionFactory;
import com.lauor.smpedr.spring.SqlSessionHolder;
import com.lauor.smpedr.spring.SqlSessionTransactionSyncAdapter;
import com.lauor.smpedr.spring.transaction.SpringManagedTransactionFactory;
import com.lauor.smpedr.transaction.TransactionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

/**
 * @description:Sql session纳入spring 事务管理工具类
 */
public class SpringSqlSessionUtil {
    private static final Logger LOG = LoggerFactory.getLogger(SpringSqlSessionUtil.class);

    /** sql session是否处于Transactional作用范围 */
    public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
        Assert.notNull(session, "No SqlSession specified");
        Assert.notNull(sessionFactory, "No SessionFactory specified");

        SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

        return holder != null && holder.getSqlSession() == session;
    }

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory) {
        Assert.notNull(sessionFactory, "No SqlSession specified");

        SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

        SqlSession session = sessionTransactionHolder(holder);
        if (session != null) {
            if ( session.isClosed() ){
                LOG.debug("SqlSession has been closed!");
            } else {
                return session;
            }
        }

        session = sessionFactory.openSession();
        //注册到spring事务管理中去
        registerSessionHolder(sessionFactory, session);
        return session;
    }

    public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
        Assert.notNull(session, "No SqlSession specified");
        Assert.notNull(sessionFactory, "No SessionFactory specified");

        SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
        if (holder != null && holder.getSqlSession() == session) {
            holder.released();
        } else {
            session.close();
        }
    }

    /** 获取Transaction范围内的sqlSession */
    private static SqlSession sessionTransactionHolder(SqlSessionHolder holder) {
        SqlSession session = null;
        if (holder != null && holder.isSynchronizedWithTransaction()) {
            holder.requested();

            session = holder.getSqlSession();
        }
        return session;
    }

    private static void registerSessionHolder(SqlSessionFactory sessionFactory, SqlSession session) {
        SqlSessionHolder holder;
        //当前线程是否存在活跃的事务同步器
        if ( TransactionSynchronizationManager.isSynchronizationActive() ) {
            TransactionFactory transactionFactory = session.getConfiguration().getTransactionFactory();

            if (transactionFactory instanceof SpringManagedTransactionFactory) {

                holder = new SqlSessionHolder(session);
                //sqlSession写入threadLocal
                TransactionSynchronizationManager.bindResource(sessionFactory, holder);
                //注册事务回调
                TransactionSynchronizationManager.registerSynchronization(new SqlSessionTransactionSyncAdapter(sessionFactory, holder));
                holder.setSynchronizedWithTransaction(true);
                holder.requested();
            } else {
                //检查是否设置了数据连接池到spring事务环境
                if (TransactionSynchronizationManager.getResource( session.getConfiguration().getDataSource() ) == null) {
                    if ( LOG.isDebugEnabled() ) {
                        LOG.debug("SqlSession [{}] was not registered for synchronization because DataSource is not transactional", session);
                    }
                } else {
                    throw new TransientDataAccessResourceException(
                            "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
                }
            }
        } else {
            if ( LOG.isDebugEnabled() ) {
                LOG.debug("SqlSession [{}] was not registered for synchronization because synchronization is not active", session);
            }
        }
    }
}
